﻿using Common;
using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// ReSharper disable InconsistentNaming
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.

#pragma warning disable CS8602 // Dereference of a possibly null reference.

namespace NewDevice
{
    /// <summary>
    /// 串口基类加强版，适用一发一收交互
    /// </summary>
    public class SerialBasePlus : IDisposable
    {
        #region 常量和字段

        /// <summary>
        /// 内部串口实例
        /// </summary>
        private readonly SerialPort? _serialPort;

        /// <summary>
        /// 接受数据缓冲区
        /// </summary>
        private readonly List<byte> _receiveBuffer = new List<byte>();

        /// <summary>
        /// 数据处理同步锁
        /// </summary>
        private readonly object _bufferLock = new object();

        /// <summary>
        /// 数据接收完成事件,供子类使用，判断是否完成接收
        /// </summary>
        public ManualResetEventSlim receivedOkDataEvent = new ManualResetEventSlim(false);

        /// <summary>
        /// 线程Token
        /// </summary>
        private readonly CancellationTokenSource? _cts;

        /// <summary>
        /// 站号
        /// </summary>
        private int _slaveId;

        /// <summary>
        /// 接收数据转换方法
        /// </summary>
        private Task? _parseTask;

        /// <summary>
        /// 上次数据接受时间
        /// </summary>
        private DateTime _lastReceivedTime = DateTime.Now;

        /// <summary>
        /// 单次数据超时时间， 单位ms
        /// </summary>
        private const int FrameTimeoutMs = 200;

        /// <summary>
        /// 单次接收超时时间
        /// </summary>
        public int ReceviceTimeOut = 1000;

        private byte[] candidateFrame
        { get; set; }

        #endregion 常量和字段

        #region 事件定义

        /// <summary>
        /// 当完整帧被解析时触发
        /// </summary>
        public event Action<byte[]>? OnFrameReceived;

        /// <summary>
        /// 当发生错误时触发
        /// </summary>
        public event Action<string>? OnError;

        /// <summary>
        /// 当有日志信息时触发
        /// </summary>
        public event Action<string>? OnLog;

        #endregion 事件定义

        #region 构造函数和初始化

        /// <summary>
        /// CTOR
        /// </summary>
        /// <param name="SlaveId"></param>
        /// <param name="portName"></param>
        /// <param name="baudRate"></param>
        public SerialBasePlus(int SlaveId, string portName, int baudRate = 9600)
        {
            _serialPort = new SerialPort(portName, baudRate)
            {
                DtrEnable = true,
                ReadTimeout = 100,
                WriteTimeout = 100,
                DataBits = 8,
                StopBits = StopBits.One,
                Parity = Parity.None,
            };

            _slaveId = SlaveId;
            _cts = new CancellationTokenSource();
        }

        /// <summary>
        /// 打开串口并启动解析线程
        /// </summary>
        public void Open()
        {
            try
            {
                _serialPort?.Open();

                //加载接收方法
                _serialPort.DataReceived += DataReceivedHandler;

                // 启动解析线程
                _parseTask = Task.Run(ParseData, _cts.Token);
                MyLog.Info($"串口 {_serialPort.PortName} 已打开，波特率: {_serialPort.BaudRate}");
                OnLog?.Invoke($"串口 {_serialPort.PortName} 已打开，波特率: {_serialPort.BaudRate}");
            }
            catch (Exception ex)
            {
                MyLog.Error(ex);
                OnError?.Invoke($"打开串口失败: {ex.Message}");
                throw;
            }
        }

        #endregion 构造函数和初始化

        #region 数据接收处理

        /// <summary>
        /// 串口数据接收事件处理
        /// </summary>
        private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                int bytesToRead = _serialPort.BytesToRead;
                if (bytesToRead == 0) return;

                byte[] buffer = new byte[bytesToRead];
                _serialPort.Read(buffer, 0, bytesToRead);

                lock (_bufferLock)
                {
                    _receiveBuffer.AddRange(buffer);
                    _lastReceivedTime = DateTime.Now;
                }

                OnLog?.Invoke($"收到 {bytesToRead} 字节数据 {ByteToString(buffer)}");
            }
            catch (Exception ex)
            {
                OnError?.Invoke($"数据接收错误: {ex.Message}");
            }
        }

        #endregion 数据接收处理

        #region 数据解析逻辑

        /// <summary>
        /// 解析线程主循环
        /// </summary>
        private async void ParseData()
        {
            while (!_cts.IsCancellationRequested)
            {
                try
                {
                    // 处理接收缓冲区
                    ProcessBuffer();

                    // 降低CPU占用
                    await Task.Delay(10);
                }
                catch (Exception ex)
                {
                    MyLog.Error(new Exception($"解析线程错误: {ex.Message}"));
                    OnError?.Invoke($"解析线程错误: {ex.Message}");
                }
            }
        }

        private string ByteToString(byte[] bytes)
        {
            return BitConverter.ToString(bytes).Replace("-", " ");
        }

        private string ByteToString(List<byte> bytes)
        {
            return BitConverter.ToString(bytes.ToArray()).Replace("-", " ");
        }

        /// <summary>
        /// 处理缓冲区中的数据
        /// </summary>
        private void ProcessBuffer()
        {
            lock (_bufferLock)
            {
                // 检查超时：如果长时间没有新数据，清空缓冲区
                if (_receiveBuffer.Count > 0 && (DateTime.Now - _lastReceivedTime).TotalMilliseconds > FrameTimeoutMs)
                {
                    MyLog.Error(new Exception($"接收超时，清空 {_receiveBuffer.Count} 字节未处理数据[{ByteToString(_receiveBuffer)}]"));
                    OnError?.Invoke($"接收超时，清空 {_receiveBuffer.Count} 字节未处理数据[{ByteToString(_receiveBuffer)}]");
                    _receiveBuffer.Clear();
                    return;
                }

                // 尝试解析帧
                while (_receiveBuffer.Count >= 2) // 至少需要帧头和长度字节
                {
                    // 如果帧头不为站号
                    if (_receiveBuffer[0] != _slaveId)
                    {
                        // 无效帧头，丢弃第一个字节
                        _receiveBuffer.RemoveAt(0);
                        continue;
                    }

                    byte frameHeader = _receiveBuffer[0];

                    // 提取候选帧
                    candidateFrame = _receiveBuffer.ToArray();

                    // 验证CRC
                    if (ValidateModbusCrc(candidateFrame))
                    {
                        // 帧有效，触发事件
                        OnFrameReceived?.Invoke(candidateFrame);
                        MyLog.Info($"成功解析帧: 头=0x{frameHeader:X2}, 长度={candidateFrame.Length}");
                        OnLog?.Invoke($"成功解析帧: 头=0x{frameHeader:X2}, 长度={candidateFrame.Length}");
                        receivedOkDataEvent.Set();
                        // 从缓冲区移除已处理帧
                        _receiveBuffer.RemoveRange(0, candidateFrame.Length);
                    }
                    else
                    {
                        MyLog.Error(new Exception($"CRC校验失败: {BitConverter.ToString(candidateFrame)}"));
                        // CRC校验失败，丢弃帧头并重试
                        OnError?.Invoke($"CRC校验失败: {BitConverter.ToString(candidateFrame)}");
                        _receiveBuffer.RemoveAt(0);
                    }
                }
            }
        }

        /// <summary>
        /// 验证Modbus CRC校验
        /// </summary>
        private bool ValidateModbusCrc(byte[] frame)
        {
            if (frame.Length < 4) return false;

            // 计算CRC的数据部分（排除最后2字节CRC）
            int dataLength = frame.Length - 2;
            ushort calculatedCrc = CalculateModbusCrc(frame, 0, dataLength);

            // 提取接收到的CRC（小端字节序：低字节在前）
            ushort receivedCrc = (ushort)(frame[frame.Length - 2] | (frame[frame.Length - 1] << 8));

            return calculatedCrc == receivedCrc;
        }

        /// <summary>
        /// 计算Modbus CRC校验码
        /// </summary>
        private ushort CalculateModbusCrc(byte[] data, int start, int length)
        {
            ushort crc = 0xFFFF;
            for (int i = start; i < start + length; i++)
            {
                crc ^= data[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((crc & 0x0001) == 1)
                        crc = (ushort)((crc >> 1) ^ 0xA001);
                    else
                        crc = (ushort)(crc >> 1);
                }
            }
            return crc;
        }

        #endregion 数据解析逻辑

        #region 数据发送方法

        /// <summary>
        /// 发送数据到串口,内部使用
        /// </summary>
        private void Send(byte[] data)
        {
            try
            {
                ushort crc = CalculateModbusCrc(data, 0, data.Length);
                byte[] send = data.Concat(BitConverter.GetBytes(crc)).ToArray();
                _serialPort.Write(send, 0, send.Length);
                MyLog.Info($"发送数据: {ByteToString(send)}");
                OnLog?.Invoke($"发送数据: {ByteToString(send)}");
            }
            catch (Exception ex)
            {
                MyLog.Error(ex);
                OnError?.Invoke($"发送错误: {ex.Message}");
                throw;
            }
        }

        //带重发机制
        public byte[] SendRepeat(byte[] data)
        {
            if (!_serialPort.IsOpen)
                throw new InvalidOperationException("串口未打开");
            int i = 0;
            while (i < 5)
            {
                Send(data);
                if (receivedOkDataEvent.Wait(ReceviceTimeOut))
                {
                    break;
                }
                i++;
            }
            receivedOkDataEvent.Reset();
            if (i == 5)
            {
                //超过最大重试次数
                throw new InvalidOperationException("发送失败");
            }
            return candidateFrame;
        }

        /// <summary>
        /// 发送字符串数据到串口
        /// </summary>
        public void Send(string text)
        {
            byte[] data = System.Text.Encoding.ASCII.GetBytes(text);
            Send(data);
        }

        #endregion 数据发送方法

        #region 资源清理

        /// <summary>
        /// 关闭串口并释放资源
        /// </summary>
        private void Close()
        {
            _cts?.Cancel();
            _serialPort.DataReceived -= DataReceivedHandler;

            if (_serialPort.IsOpen)
                _serialPort.Close();
            MyLog.Info("串口已关闭");
            //OnLog?.Invoke("串口已关闭");
        }

        /// <summary>
        /// 释放资源
        /// </summary>
        public void Dispose()
        {
            Close();
            receivedOkDataEvent?.Dispose();
            _cts.Dispose();
            _serialPort.Dispose();
        }

        #endregion 资源清理
    }
}